package com.sys.framework.web.exception;

import javax.servlet.http.HttpServletRequest;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import com.sys.common.constant.HttpStatus;
import com.sys.common.core.domain.AjaxResult;
import com.sys.common.core.text.Convert;
import com.sys.common.exception.DemoModeException;
import com.sys.common.exception.ServiceException;
import com.sys.common.utils.StringUtils;
import com.sys.common.utils.html.EscapeUtil;

import java.sql.DataTruncation;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLTimeoutException;

/**
 * 全局异常处理器
 * 
 * @author ruoyi
 */
@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    /**
     * SQL完整性约束违规异常
     */
//    @ExceptionHandler(DataIntegrityViolationException.class)
//    public AjaxResult handleDataIntegrityViolationException(DataIntegrityViolationException e) {
//        log.error(e.getMessage(), e);
//        // 解析异常信息，获取具体的错误类型
//        String errorMessage = e.getMessage();
//        if (errorMessage.contains("Duplicate entry")) {
//            return AjaxResult.error("桶编号已存在，请检查后重试");
//        } else if (errorMessage.contains("foreign key constraint fails")) {
//            return AjaxResult.error("关联数据不存在，请检查后重试");
//        } else {
//            return AjaxResult.error("数据库操作失败，请联系管理员");
//        }
//    }

    /**
     * SQL完整性约束违规异常（通用兜底）
//     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    public AjaxResult handleDataIntegrityViolationExceptionAll(DataIntegrityViolationException e) {
        log.error("数据完整性异常: {}", e.getMessage(), e);

        String errorMessage = e.getMessage();

        if (errorMessage.contains("Duplicate entry")) {
            return AjaxResult.error("数据已存在，请勿重复添加");
        } else if (errorMessage.contains("foreign key constraint fails")) {
            return AjaxResult.error("关联数据不存在，请检查引用的数据是否有效");
        } else if (errorMessage.contains("cannot be null")) {
            return AjaxResult.error("必填数据未填写完整，请检查后重试");
        } else {
            return AjaxResult.error("数据库操作失败，请检查输入数据或联系管理员");
        }
    }
    @ExceptionHandler(UncategorizedSQLException.class)
    public AjaxResult handleUncategorizedSqlException(UncategorizedSQLException e) {
        log.warn("未分类的SQL异常: {}", e.getMessage(), e);

        Throwable cause = e.getCause();
        if (cause instanceof SQLException) {
            SQLException sqlEx = (SQLException) cause;
            if ("45000".equals(sqlEx.getSQLState()) || sqlEx.getErrorCode() == 1001) {
                if (sqlEx.getMessage().contains("库存不足")) {
                    return AjaxResult.error("库存不足，无法创建订单，请检查商品库存");
                }
            }
        }

        return AjaxResult.error("数据库操作失败，请稍后重试");
    }

    /**
     * MySQL 唯一索引/主键冲突（更具体的异常）
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public AjaxResult handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        log.warn("SQL完整性约束失败（如唯一键、外键）: {}", e.getMessage());
        // 可进一步解析 getMessage() 判断是哪种约束
        if (e.getMessage().contains("Duplicate entry")) {
            return AjaxResult.error("该记录已存在，无法重复操作");
        } else if (e.getMessage().contains("foreign key")) {
            return AjaxResult.error("关联的数据不存在或已被删除");
        }
        return AjaxResult.error("数据规则校验失败，请检查输入内容");
    }

    /**
     * SQL语法错误（如表名、字段名错误）
     */
    @ExceptionHandler(org.springframework.jdbc.BadSqlGrammarException.class)
    public AjaxResult handleBadSqlGrammarException(org.springframework.jdbc.BadSqlGrammarException e) {
        log.error("SQL语法错误，请检查SQL语句: {}", e.getMessage(), e);
        return AjaxResult.error("系统配置异常，请联系管理员");
    }

    /**
     * 数据库连接失败（如数据库宕机、连接池耗尽）
     */
    @ExceptionHandler(CannotGetJdbcConnectionException.class)
    public AjaxResult handleCannotGetJdbcConnectionException(CannotGetJdbcConnectionException e) {
        log.error("数据库连接失败，请检查数据库服务状态: {}", e.getMessage(), e);
        return AjaxResult.error("服务暂时不可用，请稍后重试");
    }

    /**
     * 查询超时
     */
    @ExceptionHandler(java.sql.SQLTimeoutException.class)
    public AjaxResult handleSQLTimeoutException(SQLTimeoutException e) {
        log.warn("数据库查询超时: {}", e.getMessage());
        return AjaxResult.error("操作超时，请稍后重试");
    }

    /**
     * 锁等待超时（如行锁、表锁）
     */
    @ExceptionHandler(org.springframework.dao.CannotAcquireLockException.class)
    public AjaxResult handleCannotAcquireLockException(CannotAcquireLockException e) {
        log.warn("数据库锁等待失败: {}", e.getMessage());
        return AjaxResult.error("当前操作繁忙，请稍后重试");
    }

    /**
     * 数据被截断（如字符串太长、数值溢出）
     */
    @ExceptionHandler(java.sql.DataTruncation.class)
    public AjaxResult handleDataTruncation(DataTruncation e) {
        log.warn("数据被截断（如字段长度超限）: {}", e.getMessage());
        return AjaxResult.error("输入的内容过长或数值超出范围，请检查后重试");
    }

    /**
     * 事务异常（如超时、回滚失败）
     */
    @ExceptionHandler(org.springframework.transaction.TransactionException.class)
    public AjaxResult handleTransactionException(org.springframework.transaction.TransactionException e) {
        log.error("事务执行失败: {}", e.getMessage(), e);
        return AjaxResult.error("操作失败，请稍后重试");
    }


    /**
     * 权限校验异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限，请联系管理员授权");
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
            HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return AjaxResult.error(e.getMessage());
    }



    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
    {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        String value = Convert.toStr(e.getValue());
        if (StringUtils.isNotEmpty(value))
        {
            value = EscapeUtil.clean(value);
        }
        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.getName(), e.getRequiredType().getName(), value));
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }


    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }

    /**
     * 演示模式异常
     */
    @ExceptionHandler(DemoModeException.class)
    public AjaxResult handleDemoModeException(DemoModeException e)
    {
        return AjaxResult.error("演示模式，不允许操作");
    }



}
